﻿using System;
using System.Linq;
using Urs.Core;
using Urs.Data.Domain.Shipping;
using Urs.Core.Plugins;
using Urs.Plugin.Shipping.ByWeight.Data;
using Urs.Plugin.Shipping.ByWeight.Services;
using Urs.Services.Stores;
using Urs.Services.Configuration;
using Urs.Services.Directory;
using Urs.Services.Localization;
using Urs.Services.Shipping;

namespace Urs.Plugin.Shipping.ByWeight
{
    public class ByWeightShippingMethod : BasePlugin, IShippingRateMethod
    {
        #region Fields
        private readonly ILocalizationService _localizationService;
        private readonly IShippingService _shippingService;
        private readonly IShippingByWeightService _shippingByWeightService;
        private readonly IPriceCalculationService _priceCalculationService;
        private readonly ShippingByWeightSettings _shippingByWeightSettings;
        private readonly ShippingByWeightObjectContext _objectContext;
        private readonly ISettingService _settingService;
        private readonly IAreaService _areaService;
        private readonly IWebHelper _webHelper;

        #endregion

        #region Ctor

        public ByWeightShippingMethod(
            ILocalizationService localizationService,
            IShippingService shippingService,
            IShippingByWeightService shippingByWeightService,
            IPriceCalculationService priceCalculationService,
            ShippingByWeightSettings shippingByWeightSettings,
            ShippingByWeightObjectContext objectContext,
            ISettingService settingService,
            IAreaService areaService,
            IWebHelper webHelper)
        {
            this._localizationService = localizationService;
            this._shippingService = shippingService;
            this._shippingByWeightService = shippingByWeightService;
            this._priceCalculationService = priceCalculationService;
            this._shippingByWeightSettings = shippingByWeightSettings;
            this._objectContext = objectContext;
            this._settingService = settingService;
            this._areaService = areaService;
            this._webHelper = webHelper;
        }

        #endregion

        #region Utilities

        private decimal? GetRate(decimal subTotal, decimal weight, int shippingMethodId,
           string provinceCode, string cityCode)
        {
            var shippingByWeightRecord = _shippingByWeightService.FindRecord(shippingMethodId, provinceCode, cityCode, weight);
            if (shippingByWeightRecord == null)
                return decimal.Zero;

            //additional fixed cost
            decimal shippingTotal = shippingByWeightRecord.FirstPrice;

            if (shippingByWeightRecord.First < weight)
            {
                if (shippingByWeightRecord.Plus != 0)
                {
                    var rate = Math.Ceiling((weight - shippingByWeightRecord.First) / shippingByWeightRecord.Plus);

                    shippingTotal += shippingByWeightRecord.PlusPrice * rate;
                }
            }

            if (shippingTotal < decimal.Zero)
                shippingTotal = decimal.Zero;
            return shippingTotal;
        }

        #endregion

        #region Methods

        public GetShippingOptionResponse GetShippingOptions(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
                throw new ArgumentNullException("getShippingOptionRequest");

            var response = new GetShippingOptionResponse();

            if (getShippingOptionRequest.Items == null || getShippingOptionRequest.Items.Count == 0)
            {
                response.AddError("No shipment items");
                return response;
            }
            if (getShippingOptionRequest.ShippingAddress == null)
            {
                response.AddError("Shipping address is not set");
                return response;
            }
            var provinceName = getShippingOptionRequest.ShippingAddress.ProvinceName;
            if (!string.IsNullOrEmpty(provinceName))
                provinceName = provinceName
                    .Replace("自治区", "").Replace("特别行政区", "").Replace("壮族","").Replace("市","")
                    .Replace("维吾尔", "").Replace("回族","");
            var cityName = getShippingOptionRequest.ShippingAddress.CityName;
            if (!string.IsNullOrEmpty(cityName))
                cityName = cityName.Replace("市辖区", "");
            var province = _areaService.GetProvinceByName(provinceName);
            var city = _areaService.GetCityByName(provinceName, cityName);

            decimal subTotal = decimal.Zero;
            foreach (var shoppingCartItem in getShippingOptionRequest.Items)
            {
                if (shoppingCartItem.IsFreeShipping || !shoppingCartItem.IsShipEnabled)
                    continue;
                subTotal += _priceCalculationService.GetSubTotal(shoppingCartItem);
            }
            decimal weight = _shippingService.GetShoppingCartTotalWeight(getShippingOptionRequest.Items);

            var shippingMethods = _shippingService.GetAllShippingMethods();
            foreach (var shippingMethod in shippingMethods)
            {
                decimal? rate = GetRate(subTotal, weight, shippingMethod.Id, province?.Code, city?.Code);
                if (rate.HasValue)
                {
                    var shippingOption = new ShippingOption();
                    shippingOption.Name = shippingMethod.Name;
                    shippingOption.Description = shippingMethod.Description;
                    shippingOption.Rate = rate.Value;
                    response.ShippingOptions.Add(shippingOption);
                }
            }
            response.Success = true;

            return response;
        }


        /// <summary>
        /// Gets fixed shipping rate (if shipping rate computation method allows it and the rate can be calculated before checkout).
        /// </summary>
        /// <param name="getShippingOptionRequest">A request for getting shipping options</param>
        /// <returns>Fixed shipping rate; or null in case there's no fixed shipping rate</returns>
        public decimal? GetFixedRate(GetShippingOptionRequest getShippingOptionRequest)
        {
            if (getShippingOptionRequest == null)
                return null;

            var response = new GetShippingOptionResponse();

            if (getShippingOptionRequest.Items == null || getShippingOptionRequest.Items.Count == 0)
                return null;
            if (getShippingOptionRequest.ShippingAddress == null)
                return null;

            var provinceName = getShippingOptionRequest.ShippingAddress.ProvinceName;
            if (!string.IsNullOrEmpty(provinceName))
                provinceName = provinceName
                    .Replace("自治区", "").Replace("特别行政区", "").Replace("壮族", "").Replace("市", "")
                    .Replace("维吾尔", "").Replace("回族", "");
            var cityName = getShippingOptionRequest.ShippingAddress.CityName;
            if (!string.IsNullOrEmpty(cityName))
                cityName = cityName.Replace("市辖区", "");

            var province = _areaService.GetProvinceByName(provinceName);
            var city = _areaService.GetCityByName(provinceName, cityName);

            decimal subTotal = decimal.Zero;
            foreach (var shoppingCartItem in getShippingOptionRequest.Items)
            {
                if (shoppingCartItem.IsFreeShipping || !shoppingCartItem.IsShipEnabled)
                    continue;
                subTotal += _priceCalculationService.GetSubTotal(shoppingCartItem);
            }
            decimal weight = _shippingService.GetShoppingCartTotalWeight(getShippingOptionRequest.Items);

            var shippingMethod = _shippingService.GetAllShippingMethods().FirstOrDefault();

            decimal? rate = GetRate(subTotal, weight, shippingMethod.Id, province?.Code, city?.Code);

            return rate;
        }

        /// <summary>
        /// Gets a configuration page URL
        /// </summary>
        public override string GetConfigurationPageUrl()
        {
            return $"/Admin/ShippingByWeight/Configure";
        }

        /// <summary>
        /// Install plugin
        /// </summary>
        public override void Install()
        {
            //settings
            _settingService.SaveSetting(new ShippingByWeightSettings());

            //database objects
            _objectContext.Install();

            //locales
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.ShippingMethod", "配送方式");
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.First", "首重");
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.FirstPrice", "首费");
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.Plus", "续重");
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.PlusPrice", "增加费用");
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.AreasName", "区域");
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.AddRecord", "添加记录");
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.Default", "全国");
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.Formula", "计算公式");
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.Formula.Value", "[其它固定费用] + [首费] + ([订单总重量] - [首重])/续重 * [增加费用] + [订单小计]");
            _localizationService.AddOrUpdatePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.DivisionName", "区域名称");

            base.Install();
        }

        /// <summary>
        /// Uninstall plugin
        /// </summary>
        public override void Uninstall()
        {
            //settings
            _settingService.DeleteSetting<ShippingByWeightSettings>();

            //database objects
            _objectContext.Uninstall();

            //locales
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.ShippingMethod");
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.First");
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.FirstPrice");
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.Plus");
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.PlusPrice");
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.AreasName");
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.AddRecord");
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.Default");
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.Formula");
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.Formula.Value");
            _localizationService.DeletePluginLocaleResource("Plugins.Shipping.ByWeight.Fields.DivisionName");

            base.Uninstall();
        }

        #endregion

        #region Properties

        public ShippingRateMethodType ShippingRateMethodType
        {
            get { return ShippingRateMethodType.Offline; }
        }

        #endregion
    }
}